package com.iwdnb.gkgz.common.quota;

import java.math.BigDecimal;
import java.math.MathContext;
import java.util.ArrayList;
import java.util.List;

import com.iwdnb.gkgz.common.model.dto.StockDayData;
import com.iwdnb.gkgz.common.quota.MacdIndicator.Macd;
import com.iwdnb.gkgz.common.utils.BigDecimalUtils;

/**
 * 差异相对版macd，与标准版差异算法为每次计算ema时拿的都是当前period前作为基数ema,而不是第一个
 */
public class MacdRelative {
    public static final String QUOTA_NAME = "macd";

    private static BigDecimal calculateEMA_bak(List<BigDecimal> prices, int period) {
        BigDecimal multiplier = BigDecimal.valueOf(2).divide(BigDecimal.valueOf(period + 1), MathContext.DECIMAL128);
        BigDecimal ema = prices.get(0); // Start with the first closing price

        for (int i = 1; i < prices.size(); i++) {
            ema = (prices.get(i).subtract(ema)).multiply(multiplier).add(ema);
        }
        return BigDecimalUtils.round(ema);
    }

    public static final BigDecimal calculateEMA(final List<BigDecimal> list, final int period) {
        if (list.size() == 1) {
            return list.get(0);
        }
        // 开始计算EMA值，
        Double k = 2.0 / (period + 1.0);// 计算出系数
        Double ema = list.get(0).doubleValue();// 第一天ema等于当天收盘价
        for (int i = 1; i < list.size(); i++) {
            // 第二天以后，当天收盘价乘以系数再加上昨天EMA乘以(1-系数)
            ema = list.get(i).doubleValue() * k + ema * (1 - k);
        }
        return BigDecimalUtils.of(ema, 8);
    }

    public static List<Macd> calculateMACD(List<StockDayData> list) {
        return calculateMACD(list, 10, 20, 7);
    }

    public static List<Macd> calculateMACD(List<StockDayData> list, int shortPeriod, int longPeriod,
        int signalPeriod) {
        List<Macd> results = new ArrayList<>();
        List<BigDecimal> closingPrices = new ArrayList<>();

        for (StockDayData data : list) {
            closingPrices.add(data.getClosePrice());
        }

        List<BigDecimal> emaShort = new ArrayList<>();
        List<BigDecimal> emaLong = new ArrayList<>();
        List<BigDecimal> difLine = new ArrayList<>();
        List<BigDecimal> deaLine = new ArrayList<>();
        BigDecimal two = new BigDecimal(2);
        // Calculate EMA SHORT
        int shortTemp = shortPeriod - 1;
        int begin = 0;
        for (int i = 0; i < closingPrices.size(); i++) {
            begin = i - shortTemp;
            begin = begin > 0 ? begin : 0;
            List<BigDecimal> pricesEmaShort = closingPrices.subList(begin, i + 1);
            emaShort.add(calculateEMA(pricesEmaShort, shortPeriod));
        }

        // Calculate EMA LONG
        int longTemp = longPeriod - 1;
        for (int i = 0; i < closingPrices.size(); i++) {
            begin = i - longTemp;
            begin = begin > 0 ? begin : 0;
            List<BigDecimal> pricesEmaLong = closingPrices.subList(begin, i + 1);
            emaLong.add(calculateEMA(pricesEmaLong, longPeriod));
        }

        // Calculate DIFF (EMASHORT - EMALONG)
        for (int i = 0; i < closingPrices.size(); i++) {
            BigDecimal dif = emaShort.get(i).subtract(emaLong.get(i));
            difLine.add(dif);
        }

        // Calculate DEA line (EMA of MACD line)
        int signalTemp = signalPeriod - 1;
        for (int i = 0; i < difLine.size(); i++) {
            begin = i - signalTemp;
            begin = begin > 0 ? begin : 0;
            List<BigDecimal> subDiffList = difLine.subList(begin, i + 1);
            deaLine.add(calculateEMA(subDiffList, signalPeriod));
        }

        // Create results
        for (int i = 0; i < list.size(); i++) {
            StockDayData stockDayData = list.get(i);
            BigDecimal bar = difLine.get(i).subtract(deaLine.get(i));
            bar = BigDecimalUtils.multiply(bar, two);
            Macd macdData = new Macd(stockDayData.getDate(), stockDayData.getDatetime(), difLine.get(i),
                deaLine.get(i), bar);
            results.add(macdData);
            stockDayData.setMacd(macdData);
        }
        return results;
    }

}

